package com.common.boot.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.common.boot.exception.BusinessException;
import com.common.boot.exception.ExceptionStatus;
import com.common.boot.model.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import sun.security.util.SecurityConstants;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;

/**
 * JwtUtil
 * @author xuqq
 * @date 2020-10-23
 * @version v1.0
 */
@Slf4j
public class JwtUtil {
    /**
     * 生成签名,expireTime后过期
     * @param loginUserId 用户id
     * @param expireTime 失效时间
     * @return java.lang.String
     * @author xuqq
     * @date 202-10-23
     */
    public static String generatorJWT(String loginUserId,long expireTime) {
        //过期时间
        Date date = new Date(System.currentTimeMillis() + expireTime);
        log.info("失效时间为:{}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
        //私钥及加密算法
        Algorithm algorithm = initAlgorithm();
        //设置头信息
        HashMap<String, Object> header = initHeader();
        //附带user生成签名
        return JWT.create().withHeader(header).withClaim(ConstantUtils.ID, loginUserId).withExpiresAt(date).sign(algorithm);
    }

    /**
     * 生成签名,expireTime后过期
     * @param loginUser 用户基础信息
     * @param token       令牌
     * @param expireTime 失效时间
     * @return java.lang.String
     * @author xuqq
     * @date 202-10-23
     */
    public static String generatorAuthorization(String loginUser,String token,long expireTime) {
        //过期时间
        Date date = new Date(System.currentTimeMillis() + expireTime);
        log.info("失效时间为:{}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
        //私钥及加密算法
        Algorithm algorithm = initAlgorithm();
        //设置头信息
        HashMap<String, Object> header = initHeader();
        //附带user生成签名
        return JWT.create().withHeader(header).withClaim(ConstantUtils.USER, loginUser).withClaim(ConstantUtils.TOKEN, token).withExpiresAt(date).sign(algorithm);
    }

    /**
     *  加密用户密码
     * @param password 密码
     * @return java.lang.String
     * @author xuqq
     * @date 202-10-23
     */
    public static String encryptPassword(String password) {
        //私钥及加密算法
        Algorithm algorithm = initAlgorithm();
        //设置头信息
        HashMap<String, Object> header = initHeader();
        //附带user生成签名
        return JWT.create().withHeader(header).withClaim(ConstantUtils.PASSWORD, password).sign(algorithm);
    }

    /**
     *  加密用户密码
     * @param password 密码
     * @return java.lang.String
     * @author xuqq
     * @date 202-10-23
     */
    public static String decryptPassword(String password) {
        DecodedJWT jwt = checkAlgorithm(password);
        return jwt.getClaim(ConstantUtils.PASSWORD).asString();
    }

    /**
     * 解析用户信息
     * @author xuqq
     * @date 2021-11-02
     * @param token 令牌
     * @return com.youotech.cloud.common.model.vo.SystemUserDetailVO
     */
    public static SysUser getUserInfo(String token) {
        DecodedJWT jwt = checkAlgorithm(token);
        //获取token失效时间，如果失效则查看redis当前登录用户信息是否存在
        Date date = jwt.getExpiresAt();
        if (date.before(new Date())) {
            throw new BusinessException(ExceptionStatus.JWT_DATE_EXPIRE);
        }
        String loginUserInfo = jwt.getClaim(ConstantUtils.USER).asString();
        log.info("token解析对象，msg：{}", loginUserInfo);
        return ObjectMapperUtil.parse(loginUserInfo,SysUser.class);
    }

    /**
     * 解析用户信息
     * @author xuqq
     * @date 2021-11-02
     * @param token 令牌
     * @return com.youotech.cloud.common.model.vo.SystemUserDetailVO
     */
    public static String getUserIdByToken(String token) {
        DecodedJWT jwt = checkAlgorithm(token);
        //获取token失效时间，如果失效则查看redis当前登录用户信息是否存在
        Date date = jwt.getExpiresAt();
        if (date.before(new Date())) {
            throw new BusinessException(ExceptionStatus.JWT_DATE_EXPIRE);
        }
        return jwt.getClaim(ConstantUtils.ID).asString();
    }

    /**
     * 解析用户令牌
     * @author xuqq
     * @date 2021-11-02
     * @param authorization 令牌
     * @return com.youotech.cloud.common.model.vo.SystemUserDetailVO
     */
    public static String getLoginToken(String authorization) {
        DecodedJWT jwt = JWT.decode(authorization);
        return jwt.getClaim(ConstantUtils.TOKEN).asString();
    }

    public static String getUserIdByRequest(HttpServletRequest request) {
        String authorization = request.getHeader(ConstantUtils.AUTHORIZATION);
        DecodedJWT jwt = checkAlgorithm(authorization);
        //获取token失效时间，如果失效则查看redis当前登录用户信息是否存在
        Date date = jwt.getExpiresAt();
        if (date.before(new Date())) {
            throw new BusinessException(ExceptionStatus.JWT_DATE_EXPIRE);
        }
        return jwt.getClaim(ConstantUtils.ID).asString();
    }

    public static String getUserIdByRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String authorization = request.getHeader(ConstantUtils.AUTHORIZATION);
        DecodedJWT jwt = checkAlgorithm(authorization);
        //获取token失效时间，如果失效则查看redis当前登录用户信息是否存在
        Date date = jwt.getExpiresAt();
        if (date.before(new Date())) {
            throw new BusinessException(ExceptionStatus.JWT_DATE_EXPIRE);
        }
        return jwt.getClaim(ConstantUtils.ID).asString();
    }

    public static boolean verity(String token){
        boolean flag = false;
        try {
            Algorithm algorithm = initAlgorithm();
            JWTVerifier jwt = JWT.require(algorithm).build();
            jwt.verify(token);
            flag=true;
        } catch (IllegalArgumentException e) {
            log.error("加密异常",e);
        } catch (JWTVerificationException e) {
            log.error("验证异常",e);
        }
        return flag;
    }

    private static DecodedJWT checkAlgorithm(String data){
        DecodedJWT jwt = JWT.decode(data);
        Algorithm algorithm = initAlgorithm();
        if (algorithm.getName().equals(jwt.getAlgorithm())) {
           return jwt;
        } else {
            throw new BusinessException(ExceptionStatus.JWT_SIGN_EXPIRE);
        }
    }

    private static HashMap<String, Object> initHeader(){
        HashMap<String, Object> header = new HashMap<>(2);
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        return header;
    }

    private static Algorithm initAlgorithm(){
        return Algorithm.HMAC256(SecurityConstants.SOCKET_CONNECT_ACTION);
    }

}
